<!DOCTYPE html>
<html lang='zh-Hans'>

<head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <base href=''>
    <!--<script src='/lab/resources/eruda.js'></script>
    <script>eruda.init()</script>-->
    <title>合约测试</title>
    <link rel='stylesheet' href='/lab/resources/bootstrap-5.1.3/bootstrap.min.css'>
</head>

<body>
    <div class='container' id='root'>
        <h3 class='d-flex justify-content-center'>{{title}} <small>{{this.connected ? "已连接" : "未连接"}}</small>  </h3>
        <div class="row gy-2">
            <div class="col-sm-6 col-12">
                <div class="input-group input-group-sm">
                    <span class="input-group-text">发送地址</span>
                    <input type="text" class="form-control" v-model="acct_address">
                </div>
            </div>
            <div class="col-sm-6 col-12">
                <div class="input-group input-group-sm">
                    <span class="input-group-text">作参地址</span>
                    <input type="text" class="form-control" v-model="recv_address">
                </div>
            </div>
            <div class="col-sm-6 col-12">
                <div class="input-group input-group-sm">
                    <span class="input-group-text">合约地址</span>
                    <input type="text" class="form-control" v-model="contract_address">
                </div>
            </div>
            <div class="col-sm-6 col-12">
                <div class="input-group input-group-sm">
                    <span class="input-group-text">处理数量</span>
                    <input type="text" class="form-control" v-model="amount">
                </div>
            </div>
            <div class="col-12">
                <textarea class="form-control" rows="3" v-model="contract_abi" placeholder="输入合约ABI"></textarea>
            </div>
            <div class="col-12">
                <input type="text" class="form-control" v-model="contract_param" placeholder="请输入构造参数（多个用空格分隔)">
                <textarea class="form-control" rows="3" v-model="contract_code" placeholder="输入合约字节码用来部署合约"></textarea>
            </div>
            <div class="col-12">
                <div class=" flex justify-content-around">
                    <button class="btn btn-primary btn-sm" @click="deployContract">部署合约</button>
                    <button class="btn btn-primary btn-sm" @click="senderToken">发送代币</button>
                    <button class="btn btn-primary btn-sm" @click="getContractInfo">合约信息</button>
                    <button class="btn btn-primary btn-sm" @click="setContract">设置合约</button>
                </div>
            </div>
        </div>
        <div class="row gy-2 gx-1 mt-3">
            <div class="col-sm-6 col-12">
                <div class="card">
                    <div class="card-body">
                        <div class="card-title lead">合约方法</div>
                        <div class="list-group" v-if="contract_info.methods" @click.capture="callContractMethod">
                            <template v-for="(value,key) in contract_info.methods">
                                <div class="list-group-item font-monospace" :key="key" v-if="/.*\(.*\)/.test(key)">
                                    <!-- <span class="w-25"></span> -->{{key}}
                                </div>
                            </template>
                            </div>
                        </div>
                        <div class="card-footer" v-if="eventArr.length > 0">
                            <div class="flex justify-content-around flex-wrap">
                                <template v-for="name of eventArr">
                                    <button class="mx-1 btn btn-outline-secondary btn-sm" :id="name" @click="doListen" :key="name">
                                        {{name}}
                                    </button>
                                </template>
                            </div>
                            <!-- <div class="input-group">
                                <input type="text" class="form-control" v-model="listenEventName">
                                <button class="btn btn-outline-primary">监听</button>
                            </div> -->
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script src='/lab/resources/bootstrap-5.1.3/bootstrap.bundle.min.js'></script>
    <script src='/lab/resources/vue.js'></script>
    <script src="lib/web3.min.js"></script>
    <script src="../dist/main.bundle.js"></script>
    <script>
        const {ConnectWallet, ContractGet, ContractGetInfo, CallContractMethod, ContractSet, Utils} = window.st_web3lab
        const {getFuncParameterArr} = Utils
        const app = new Vue({
            el: '#root',
            computed: {
                contract_contruct_param() {
                    if (this.contract_param && this.contract_param.trim()) {
                        return this.contract_param.trim().split(" ").filter(v => v && v.trim()).map(v => {
                            // try {
                            //     return parseInt(v)
                            // } catch (e) {
                            //     console.error(`transfer ${v} err: ${e.message}`)
                            //     return v
                            // }
                            return v
                        });
                    } else {
                        return []
                    }
                }
            },
            data() {
                this.w3 = null;
                this.eventListener = {}
                return {
                    title: '合约测试',
                    connected: true,
                    acct_address: "",
                    recv_address: "0x92297eb1857f3e8a5f804d0a1e03e6020fd5a530",
                    // contract_address: "0xeDB85DbB4D9bcd74eaAE30D1D9Cc71e560f29Bb7",
                    contract_address: "0xf2457136C1d156cF002ef770d9c913F8C2697f40",
                    contract_abi: "",
                    contract_code: "",
                    contract_param: "",
                    contract_info: {},
                    amount: 10,
                    eventArr: [],
                }
            },
            methods: {
                async deployContract() {
                    if (!this.contract_code || !this.contract_code.trim() || !this.contract_abi || !this.contract_abi.trim()) {
                        alert("Need contract bin code!")
                        return;
                    }
                    
                    try {
                        let code = "0x" + this.contract_code
                        console.log("param:", this.contract_contruct_param)
                        let contract = new this.w3.eth.Contract(JSON.parse(this.contract_abi), undefined, {
                            from: this.acct_address,
                        })
                        let deployFunc = contract.deploy({
                            data: code,
                            arguments: this.contract_contruct_param,
                        })
                        let deployResult = await deployFunc.send()
                        console.log("%c[发布结果]", "background:blue; color:white; padding:5px;", deployResult)
                        if (deployResult.options && deployResult.options.address) {
                            alert("发布成功!")
                            this.contract_address = deployResult.options.address
                            console.log("发布合约地址:", deployResult.options.address)
                        }
                    } catch (e) {
                        console.error("deploy err:", e)
                        alert(e.message)
                    }
                    

                },
                eventDataCallback(v) {
                    console.log("get event", v)
                    if (v.raw && v.raw.data) alert(v.raw.data)
                    else alert(v.event + " appear!")
                },
                eventConnectCallback(v) {
                    console.log("connect!", v)
                },
                async doListen(e) {
                    let a = e.currentTarget;
                    let eventName = a.getAttribute("id")
                    if (this.eventListener[this.contract_address] && this.eventListener[this.contract_address][eventName]) {
                        alert("has listened")
                        return
                    } else {
                        if (!this.eventListener[this.contract_address]) {
                            this.eventListener[this.contract_address] = {}
                        }
                    }
                    if (eventName) {
                        let contract = await ContractGet(this.w3, this.contract_abi, this.contract_address)
                        let eventEmitter = contract.events[eventName]()
                        this.eventListener[this.contract_address][eventName] = eventEmitter
                        eventEmitter.on("connected", this.eventConnectCallback)
                        eventEmitter.on("data", this.eventDataCallback)
                        return "ok"
                    } else {
                        alert("attribute id not setted.")
                        return false
                    }
                },
                async setContract() {
                    await ContractSet(
                        this.w3,
                        this.contract_abi,
                        this.contract_address,
                        {
                            from: this.acct_address,
                        }
                    )
                },
                async getContractInfo() {
                    if (!this.w3 || !this.contract_address) {
                        alert("input w3/contractAddr")
                        return;
                    }
                    // await 
                    console.log("GET CONTRACT INFO.")
                    let contract = await ContractSet(
                        this.w3,
                        this.contract_abi,
                        this.contract_address,
                        {
                            from: this.acct_address,
                        }
                    )
                    console.log("cotnract:", contract)
                    ContractGetInfo(this.w3, this.contract_abi, this.contract_address).then(v => {
                        console.log("get contract info:", v)
                        this.contract_info = v
                        this.eventArr = v.events
                    }).catch(e => {
                        console.error("contract info get err:", e)
                    })
                },
                senderToken() {

                },
                paramGet(parameterNameArr, methodName) {
                    switch(methodName) {
                        case "allowance":
                            return [this.acct_address, this.recv_address]
                        case "transferFrom":
                            return [this.recv_address, this.acct_address, this.amount]
                        default:
                            console.warn("Current not set special deal for method:", methodName)
                    }
                    let map = {
                        address: this.recv_address,
                        uint256: this.amount,
                    }
                    let resultArr = []
                    
                    return parameterNameArr.filter(v => map[v]).map(v => map[v])
                },
                methodGet(methodSign) {
                    let callFuncArr = [
                        /balances\(.*/,
                        /minter\(.*/,
                        /name\(.*/,
                        /decimals\(.*/,
                        /symbol\(.*/,
                        /totalSupply\(.*/,
                        /creator\(.*/,
                        /balanceOf\(.*/,
                        /allowance\(.*/,
                    ]
                    for (let x of callFuncArr) {
                        if (x.test(methodSign)) {
                            return "call"
                        }
                    }
                    return "send"
                },
                callContractMethod(e) {
                    methodSign = e.target.innerText
                    // console.log('prepare call:', methodSign)
                    parameterArr = getFuncParameterArr(methodSign)
                    parameterV = this.paramGet(parameterArr, /(.*)\(.*\)/.exec(methodSign)[1])
                    console.log(parameterV)
                    let methodType = this.methodGet(methodSign)
                    CallContractMethod(this.w3, this.contract_abi, this.contract_address, methodSign, parameterV, methodType)
                        .then(v => {
                            console.log(`${methodType} ${methodSign}(${parameterV}) result: `, v)
                        })
                        .catch(e => {
                            console.error(`${methodType} ${methodSign}(${parameterV}) err`, e)
                            if (e.message) alert(e.message)
                        })
                },
                acctChanged() {
                   this.getConnectedAcct();
                },
                getConnectedAcct() {
                    this.w3.eth.getCoinbase().then(addr => {
                        this.acct_address = addr;
                    }).catch(e => {
                        console.error("getCoinbase err:", e)
                    })
                },
                
            },
            mounted() {
                ConnectWallet(this.acctChanged).then(w3 => {
                    this.w3 = w3;
                    this.connected = true;
                    // window.w3 = w3;
                    this.getConnectedAcct();
                }).catch( e => {
                    console.error(e)
                    alert(e.message || e)
                })
            }
        })
    </script>
</body>

</html>